using System;
using System.Collections.Generic;
using System.Text;
using CodePlex.SpaceBlock.UI.Controls;
using System.Windows.Forms;
using CodePlex.Resourceful;
using System.Xml;
using System.Threading;
using CodePlex.SpaceBlock.UI.Controls.Tools;

namespace CodePlex.SpaceBlock.UI
{
    public static class AuthUtils
    {

        public static event EventHandler FileSystemManagerChanged;

        private static FileSystemManager __fileSystemManager;

        public static FileSystemManager FileSystemManager
        {
            get
            {
                return __fileSystemManager;
            }
        }

        public static bool AppStartup(MainForm form)
        {
            if (string.IsNullOrEmpty(Properties.Settings.Default.PublicSettings) || string.IsNullOrEmpty(Properties.Settings.Default.PrivateSettings))
            {
                ResetMasterPassword(form,false,true);
            }
            else
            {
                string password = null;
                IEnumerable<NameValuePairs> privateSettings = null;

                while (privateSettings == null)
                {
                    password = PasswordPrompt(form,true);
                    if (password == null)
                    {
                        return false;
                    }
                    privateSettings = IsPasswordValid(password);
                }

                IEnumerable<FileSystemInfo> infos = BuildInfos(privateSettings);
                Reinitialize(infos, password,false);
                if (!AuthUtils.FileSystemManager.IsValid)
                {
                    if (!form.ShowToolsOptions(true))
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        public static string ResetMasterPassword(MainForm form, bool promptForCurrentPassword, bool exitOnCancel)
        {
            return ChangePasswordForm.Show(form, promptForCurrentPassword, delegate
            {
                if (exitOnCancel)
                {
                    Application.Exit();
                }
            }, delegate(string oldPassword, string newPassword)
            {
                return NewPrivateData(form, oldPassword, newPassword);
            });
        }

        public static bool Validate(MainForm form, IEnumerable<FileSystemInfo> infos)
        {
            string password = null;

            IEnumerable<NameValuePairs> privateSettings = null;
            while (privateSettings == null)
            {
                password = PasswordPrompt(form, false);

                if (password == null)
                {
                    return false;
                }

                privateSettings = IsPasswordValid(password);
            }

            AuthUtils.Reinitialize(infos, password, true);

            return true;


        }


















        private static void OnFileSystemManagerChanged()
        {
            if (FileSystemManagerChanged != null)
            {
                FileSystemManagerChanged(null, EventArgs.Empty);
            }
        }

        private static IEnumerable<FileSystemInfo> BuildInfos(IEnumerable<NameValuePairs> privateSettings)
        {
            IEnumerable<NameValuePairs> publicSettings = FromXml(Properties.Settings.Default.PublicSettings);
            List<NameValuePairs> privateSettingsList = new List<NameValuePairs>();
            if (privateSettings != null)
            {
                privateSettingsList.AddRange(privateSettings);
            }

            List<FileSystemInfo> infos = new List<FileSystemInfo>();
            foreach (NameValuePairs publicSetting in publicSettings)
            {
                FileSystemInfo info = new FileSystemInfo(publicSetting, privateSettingsList.Find(delegate(NameValuePairs item)
                {
                    return item["ID"] == publicSetting["ID"];
                }));
                infos.Add(info);
            }

            return infos;

        }

        private static bool NewPrivateData(MainForm form, string oldPassword, string newPassword)
        {

            bool zombieMode = false;
            bool showToolsOptions = true;
            IEnumerable<FileSystemInfo> infos = null;
            if (oldPassword != null)
            {
                if (oldPassword == "")
                {
                    infos = BuildInfos(null);
                    UIUtils.MessageBox(Properties.Messages.ChangePassword_Reverify);
                    zombieMode = true;
                }
                else
                {
                    IEnumerable<NameValuePairs> privateSettings = IsPasswordValid(oldPassword);
                    if (privateSettings == null)
                    {
                        UIUtils.MessageBox(Properties.Messages.ChangePassword_BadCurrentPassword);
                        return false;
                    }

                    infos = BuildInfos(privateSettings);
                    UIUtils.MessageBox(Properties.Messages.ChangePassword_Changed);
                    showToolsOptions = false;
                   
                }

               
             
            }

            Reinitialize(infos, newPassword, true);

            if (showToolsOptions)
            {
                form.ShowToolsOptions(zombieMode);
            }
           
            return true;

        }


        private static void Reinitialize(IEnumerable<FileSystemInfo> infos, string password, bool save)
        {
            List<FileSystemInfo> newInfos = new List<FileSystemInfo>();
            if (infos != null)
            {
                newInfos.AddRange(infos);
            }
            if (newInfos.Count == 0)
            {
                FileSystemInfo info = new FileSystemInfo();
                info.Type = "Local";
                info.Caption = "Local File System";
                newInfos.Add(info);
            }

           
            __fileSystemManager = new FileSystemManager(newInfos);


            if (save)
            {
                // save settings
                string publicSettings = ToXml(newInfos.ConvertAll<NameValuePairs>(delegate(FileSystemInfo item)
                {
                    return item.PublicSettings;
                }));
                string privateSettings = ToXml(newInfos.ConvertAll<NameValuePairs>(delegate(FileSystemInfo item)
                {
                    return item.PrivateSettings;
                }));

                privateSettings = SpaceBlockUtils.Encrypt(privateSettings, password);

                Properties.Settings.Default.PublicSettings = publicSettings;
                Properties.Settings.Default.PrivateSettings = privateSettings;
                Properties.Settings.Default.Save();
            }

            OnFileSystemManagerChanged();
            



        

        }

        private static IEnumerable<NameValuePairs> FromXml(string xml)
        {
            List<NameValuePairs> rt = new List<NameValuePairs>();

            XmlDocument doc = new XmlDocument();

            doc.LoadXml(xml);

            foreach (XmlElement nvpElement in doc.SelectNodes("/Settings/NameValuePairs"))
            {
                rt.Add(FromXml(nvpElement));
            }

            return rt;
        }

        private static NameValuePairs FromXml(XmlElement nvpElement)
        {
            NameValuePairs rt = new NameValuePairs();

            foreach (XmlElement e in nvpElement.SelectNodes("Key"))
            {
                rt[e.Attributes["Name"].Value] = e.InnerText;
            }

            return rt;
        }

        private static string ToXml(IEnumerable<NameValuePairs> nvps) 
        {
            StringBuilder sb = new StringBuilder();
            using (XmlWriter writer = XmlWriter.Create(sb))
            {
                writer.WriteStartElement("Settings");
                foreach (NameValuePairs nvp in nvps)
                {
                    writer.WriteStartElement("NameValuePairs");
                    foreach (string key in nvp.Keys)
                    {
                        writer.WriteStartElement("Key");
                        writer.WriteAttributeString("Name", key);
                        writer.WriteString(nvp[key]);
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
                writer.Flush();
            }
            return sb.ToString();
        }


        private static string PasswordPrompt(MainForm form, bool showReset)
        {
            string customButtonText = null;
            Fn<string> customButtonAction = null;
          
            if (showReset)
            {
                customButtonText = "Reset";
                customButtonAction = delegate
                {
                    return ResetMasterPassword(form,true,true);
                };
            }

            string description = showReset ?
                Properties.Messages.PasswordPrompt_Description_Load :
                Properties.Messages.PasswordPrompt_Description_Save;

            return UIUtils.Inputbox(null,
                       UIManager.Instance.GetIcon(UIConstants.ImageHints.Key),
                       Properties.Messages.PasswordPrompt_Title,
                       description,
                       "Password:",
                       true,
                       null,
                       customButtonText,
                       customButtonAction);

        }

        private static IEnumerable<NameValuePairs> IsPasswordValid(string password)
        {
            try
            {
                // validate password
                string cipherText = Properties.Settings.Default.PrivateSettings;
                string plainText = SpaceBlockUtils.Decrypt(cipherText, password);
                return FromXml(plainText);
                
            }
            catch
            {

            }
            return null;
        }


    }


   
}
